/** 
 * @author Riccardo Manfrin [namesurname at gmail dot com]
 * 
 * @brief  Transmitter/Receiver file parser
 * 
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h> /* usleep */
#include <arpa/inet.h> /*inet_addr*/
#include "data_sock.h"
#include "fileparser.h"
#include "rdata.h"
#include "wifi.h"
#include "control.h"
#include "debug.h"
#include "defines.h"

enum {
	CTRL_LINK_MISSING_INFO,
	TCP_MISSING_IP_OR_PORT,
	MISSING_DATA_IPIFACE
} err_code;

static unsigned int lat = 0;
static unsigned int pktsz = 0;
static unsigned int npkts = 1;

static void error(int e);
static void set_lat(unsigned int i);
static void set_npkts(unsigned int i);
static void set_pktsz(unsigned int i);

static void error(int e)
{
	printf("fileparser.c : ERR_CODE %i\n",(int)e);
	return;
}

static void set_lat(unsigned int i)
{
	lat = i;
	return;
}
static void set_npkts(unsigned int i)
{
	npkts = i;
	return;
}
static void set_pktsz(unsigned int i)
{
	pktsz=i;
	return;
}

void parser_tx_file(char *cmdline){
  int i;
  char **args;
  char *ptr;
  char *cmd = malloc(CMD_SZ);
  unsigned char cmdallowed=0;
  memset(cmd,0,CMD_SZ);
  sscanf(cmdline, "%s", cmd);
  if(memcmp(cmd,"#",1)==0) return;
  if(memcmp(cmdline,"\n",1)==0) return;
  
  args = malloc(sizeof(char *) * MAX_ARGS);
  i = 0;
  ptr = cmdline + strlen(cmd); // Pointer to next token of the string.
  for(i = 0; i < MAX_ARGS; i++)
  {
    args[i] = malloc(ARG_SZ);
    memset(args[i],0,ARG_SZ);
    sscanf(ptr, "%s", args[i]);
    ptr = ptr + strlen(args[i]) + 1;
  }
  ctrl_ack *ack=NULL;
  /**
    * While UDP does not require this check,
    * TCP data streams need a correspondent server
    * to accept the connection tries.
    * Currently only UDP is permitted.
    */
  //If we have a control channel
  if(ctrl_cnt_cce()==0){
    //Transmit the command to the server
    ctrl_cnt_send(cmdline,strlen(cmdline));
    //Wait for the server reply & process it.
    ack=ctrl_cnt_wack();
    //we HAVE a CC => check the result
    if(ack->result==1) cmdallowed=1;
    else
    {
      dbg_printf(5, "fileparser.c: \tCOMMAND SKIPPED\n");
      if(ack->action==OPENDATA)
      {
        if(REASON(OPENDATA,NOWIFI)){
          
	  /** 
	   * Disabling wifi as the server does not have
	   * We skip command because with a non wifi enabled
	   * server, there's no reason to send data packets 
	   * through a wireless interface
	   */
          wifi_setEn(SFSD);
        }
      }
    }
  }
  //if we have DON'T have a CC => skip the CC ack
  else cmdallowed=1;
  if(cmdallowed==1)
  {
    if(memcmp("openctrl",cmd,8)==0)
    {
      int srcipiface=0,dstip=0,dstport=0;
      /** 
      struct sockaddr_in { 
        sa_family_t     sin_family; // address family: AF_INET 
        in_port_t       sin_port;   // port in network byte order
        struct in_addr  sin_addr;   // internet address
      }; 
      // Internet address
      struct in_addr {
        in_addr_t       s_addr;     // address in network byte order
      };
      **/
      struct sockaddr_in src,dst;
      /**
        * Here we setup the TCP control connection
        * to send notifications to the server.
        */
      //enabling control channel
      ctrl_cnt_set_cce();
                
      memset(&src,0,sizeof(struct sockaddr_in));
      memset(&dst,0,sizeof(struct sockaddr_in));
      src.sin_family=AF_INET;
      dst.sin_family=AF_INET;
      for (i=0;i<MAX_ARGS;i++)
      {
        if(memcmp(args[i],"srcip=",6)==0) 
        {
          srcipiface=1;
          src.sin_addr.s_addr=inet_addr((char*)args[i]+6);
        }
        if(memcmp(args[i],"iface=",6)==0) {
          srcipiface=1;
          ctrl_set_iface((char*)(args[i]+6));
        }
        if(memcmp(args[i],"dstip=",6)==0)
        {
          dstip=1;
          dst.sin_addr.s_addr=inet_addr((char*)args[i]+6);
        }
        if(memcmp(args[i],"srcport=",8)==0)
        {
          /**
            * Non mandatory argument: this forces the
            * TCP source port to be a certain one on the  
            * client. If not specified in the command
            * src.sin_port is not filled and a random 
            * TCP source port will be picked up by the
            * client.
            * Note that when a specific port is used,
            * the ACK/FIN/FINACK mechanism keeps the 
            * client TCP port open in a TIMEWAIT state
            * until a certain amount of time has elapsed,
            * which is not a desirable feature.
            */
          src.sin_port=atoi((char*)(args[i]+8));
        }
        if(memcmp(args[i],"dstport=",8)==0)
        {
          dstport=1;
          dst.sin_port=atoi((char*)(args[i]+8));
        }
      }
      if(srcipiface==0 || dstip==0 || dstport==0)
      {
              error(CTRL_LINK_MISSING_INFO);
              return;
      }
      else ctrl_cnt_setup(&src,&dst);
    }
    else if(memcmp("opendata",cmd,8)==0)
    {
      int tcp=0; int tcp_dstip=0; int tcp_dstport=0;
      for (i=0;i<MAX_ARGS;i++)
      {
        /**
        * Hereby we specify the data connection setup:
        * TCP data streams are still not available,
        * even if the logic behind those is anyway 
        * implemented (such as checks on the presence
        * of remote ip and tcp port to connect to.
        */
        if(memcmp(args[i],"tcp",3)==0) {ds_set_transport(TCP);tcp=1;}
        if(memcmp(args[i],"udp",3)==0) ds_set_transport(UDP);
        if(memcmp(args[i],"uc",2)==0) ds_set_skt_txmode(UC);
        if(memcmp(args[i],"bc",2)==0) ds_set_skt_txmode(BC);
        if(memcmp(args[i],"mc",2)==0) ds_set_skt_txmode(MC);
        if(memcmp(args[i],"iface=",6)==0) ds_set_iface((char*)(args[i]+6));
        if(memcmp(args[i],"srcip=",6)==0) ds_set_src_addr((char*)(args[i]+6));
        if(memcmp(args[i],"dstip=",6)==0) {ds_set_dst_addr((char*)(args[i]+6)); tcp_dstip=1;}
        if(memcmp(args[i],"port=",5)==0) ds_set_src_port(atoi((char*)(args[i]+5)));
        if(memcmp(args[i],"dstport=",8)==0) {ds_set_dst_port(atoi((char*)(args[i]+5))); tcp_dstport=1;}
        if(memcmp(args[i],"wlan",4)==0) 
        {
          //Shouldn't reach this if we deny wifi @ the server
          if(wifi_getEn()==SFSD) wifi_setEn(WIFIDISABLED);
	  else wifi_setEn(WIFIENABLED);
        }
        
        if(tcp==1 && (tcp_dstip==0 || tcp_dstport==0))
        {
                error(TCP_MISSING_IP_OR_PORT);
                return;
        }
        //Downgrading the socket to UDP because information is missing.
        if(tcp==1) ds_set_transport(UDP);
      }
      ds_open();
    }
    else if(memcmp("set",cmd,3)==0)
    {
      sscanf(cmdline, "%*s %s %s", args[0], args[1]);
      for (i=0;i<MAX_ARGS;i++)
      {
        if(memcmp(args[i],"rate=",5)==0)
        {
          if(wifi_getEn()==WIFIENABLED) wifi_setRate(atof((char*)(args[i]+5)));
        }
        if(memcmp(args[i],"channel=",8)==0)
        {
          if(wifi_getEn()==WIFIENABLED) wifi_setCh(atof((char*)(args[i]+8)));
        }
      }
    }
    else if(memcmp("sleep", cmd, 5)==0)
    {
      int l;
      sscanf(cmdline, "%*s %i", &l);
      usleep(l);
    }
    else if(memcmp("send",cmd,4)==0)
    {
      int err = 0;
      for (i=0;i<MAX_ARGS;i++)
      {
        //printf("SEND - %s\n",args[i]);
        if(memcmp(args[i],"ip=",3)==0) ds_set_dst_addr((char*)(args[i]+3));
        if(memcmp(args[i],"port=",5)==0) ds_set_dst_port(atoi((char*)(args[i]+5)));
        if(memcmp(args[i],"lat=",4)==0) set_lat(atoi((char*)(args[i]+4)));
        if(memcmp(args[i],"npkts=",6)==0) set_npkts(atoi((char*)(args[i]+6)));
        if(memcmp(args[i],"sz=",3)==0) set_pktsz(atoi((char*)(args[i]+3)));
        if(memcmp(args[i],"static",6)==0) {rdata_init(STATIC);}
        if(memcmp(args[i],"dynamic",7)==0) {rdata_init(DYNAMIC);}
        if(memcmp(args[i],"sequence",7)==0) {rdata_set(SEQUENCE, 1);}
	if(memcmp(args[i],"wlanchan",8)==0) {rdata_set(WLANCHAN, 8);}
        else rdata_set(SEQUENCE, 0);
      }
      for(i=0;i<npkts;i++)
      {
        err = ds_send(rdata_gen(pktsz),pktsz);
        //Note that usleep is not accurate
        if(lat>0) usleep(lat);
      }
      if(err>=0) dbg_printf(5, "parser.c: \tSENT\n");
    }
    else if(memcmp("closedata",cmd,9)==0)
    {
      dbg_printf(5, "parser.c: \tDATA CONNECTION CLOSED\n");
      rdata_free();
      ds_close();
    }
    else
    {
          /** 
            * Non supported command or other stuff
            */
  }	
  }
  free(cmd);
  for(i = 0; i < MAX_ARGS; i++)
  {
          free(args[i]);
  }
  return;
}

void parser_rx_file(char *cmdline){
  int i;
  char **args;
  char *ptr;
  char *cmd = malloc(CMD_SZ);
  memset(cmd,0,CMD_SZ);
  sscanf(cmdline, "%s", cmd);
  if(memcmp(cmd,"#",1)==0) return;
  
  args = malloc(sizeof(char *) * MAX_ARGS);
  i = 0;
  ptr = cmdline + strlen(cmd);
  for(i = 0; i < MAX_ARGS; i++)
  {
          args[i] = malloc(ARG_SZ);
          memset(args[i],0,ARG_SZ);
          sscanf(ptr, "%s", args[i]);
          ptr = ptr + strlen(args[i]) + 1;
  }
  
  if(memcmp("open",cmd,4)==0)
  {
    /**
      * Here we setup the TCP control connection
      * to send notifications to the server.
      * We also specify the interface for incoming
      * data in order to tune it appropriately.
      */
    int ctrl_ipiface=0,ctrl_port=0,data_ipiface;
    /** 
    struct sockaddr_in { 
      sa_family_t     sin_family; // address family: AF_INET 
      in_port_t       sin_port;   // port in network byte order
      struct in_addr  sin_addr;   // internet address
    }; 
    // Internet address
    struct in_addr {
      in_addr_t       s_addr;     // address in network byte order
    };
    **/
    struct sockaddr_in src;
    memset(&src,0,sizeof(struct sockaddr_in));
    src.sin_family=AF_INET;
    for (i=0;i<MAX_ARGS;i++)
    {
      if(memcmp(args[i],"ctrl_ip=",8)==0) 
      {
        ctrl_ipiface=1;
        src.sin_addr.s_addr=inet_addr((char*)args[i]+8);
      }
      if(memcmp(args[i],"ctrl_iface=",11)==0) {
        ctrl_ipiface=1;
        ctrl_set_iface((char*)(args[i]+11));
      }
      if(memcmp(args[i],"ctrl_port=",10)==0)
      {
        ctrl_port=1;
        src.sin_port=atoi((char*)(args[i]+10));
      }
      if(memcmp(args[i],"data_ip=",8)==0) 
      {
        data_ipiface=1;
        ds_set_src_addr((char*)(args[i]+8));
      }
      if(memcmp(args[i],"data_iface=",11)==0) {
        data_ipiface=1;
        ds_set_iface((char*)(args[i]+11));
      }
      if(memcmp(args[i],"wlan",4)==0)
      {
        wifi_setEn(WIFIENABLED);
      }
    }
    if(ctrl_ipiface==0 || ctrl_port==0)
    {
      error(CTRL_LINK_MISSING_INFO);
      return;
    }
    else
    {
      ctrl_srv_setup(&src);
    }
    if(data_ipiface==0)
    {
      error(MISSING_DATA_IPIFACE);
      return;
    }
  }

  else
  {
  /** 
    * Non supported command or other stuff
    */
  }
  free(cmd);
  for(i = 0; i < MAX_ARGS; i++)
  {
          free(args[i]);
  }
  return;
}
